package com.mlnx.ecg.ui.view;

import com.alibaba.fastjson.JSON;
import com.mlnx.ecg.ui.config.ECGDeviceConstant;
import com.mlnx.ecg.ui.model.Point;
import com.mlnx.ecg.ui.model.RectF;
import com.mlnx.ecg.ui.model.WavePos;
import com.mlnx.ecg.ui.style.WaveStyle;

import java.awt.*;


public class WaveView {

    protected Point lTop; // 左上
    protected Point lBot; // 左下
    protected Point rTop; // 右上
    protected Point rBot; // 右下

    protected float margin = 0;
    protected float svCell = 0; // 垂直方向cell 尺寸
    protected float shCell = 0; // 水平方向cell 尺寸
    protected float xCellNumb; // 水平方向cell个数
    protected float left;
    protected float right;
    protected float top;
    protected float bottom;
    private RectF rFrame;

    private int flag; // 波形标记

    // 显示的点的个数
    protected int np;
    protected Point[] points;
    protected int cur_point = 0;
    protected float offset;

    // X点间距
    protected float dataXInterval;

    // 动画状态
    protected boolean animing;

    // 首次绘制
    protected boolean fristRound = true;

    // 描述
    protected String describe;

    protected boolean showGridding;

    protected boolean drawCurrentPoint = true;

    private int showFre;

    // 显示尺寸参数
    private float mmPermV = ECGDeviceConstant.DEFAULT_SCALE; // mm/mv
    private float mmPerS = ECGDeviceConstant.DEFAULT_SPEED; // mm/s
    private float mmPerPixelVertical = 0.28889f; // 垂直方向 mm/像素点间距
    private float mmPerPixelHorizontal = 0.2847f; // 水平方向 mm/像素点间距
    protected float cellNumbPremV = 2; // 多少个框代表一个mv
    private float cellNumbPreS = 5; // 多少个框代表 1s

//    public final float INVALID_DATA = 0.123456f;

    public WaveView(WavePos wavePos, int flag, String describe, int showFre,
                    float mmPermV, float mmPerS) {
        this.lTop = wavePos.getlTop();
        this.lBot = wavePos.getlBot();
        this.rTop = wavePos.getrTop();
        this.rBot = wavePos.getrBot();
        this.flag = flag;
        this.describe = describe;
        this.mmPermV = mmPermV;
        this.mmPerS = mmPerS;
        this.showFre = showFre;

        init();
    }

    public synchronized void init() {
        cur_point = 0;

        float h = lBot.y - lTop.y;
        // float w = rTop.x - lTop.x;

        margin = h * WaveStyle.ECG_FRAME_MARGIN_RATIO;
        left = lTop.x + margin;
        right = rTop.x - margin;
        top = lTop.y + margin;
        bottom = lBot.y - margin;

        svCell = mmPermV / mmPerPixelVertical / cellNumbPremV;
        shCell = mmPerS / mmPerPixelHorizontal / cellNumbPreS;

        xCellNumb = ((right - left) / shCell);
        rFrame = new RectF(left, top, right, bottom);

        // 容器能显示的点个数
        np = (int) (xCellNumb * showFre / cellNumbPreS);

        // 两点间距离
        dataXInterval = (right - left) / np;

        // 空出前两格
        offset = (2 * showFre / cellNumbPreS) * dataXInterval;

        np -= (int) (2 * showFre / cellNumbPreS);
        if (np <= 0)
            np = 100;

        // 初始化点的位置
        points = new Point[np];

        for (int i = 0; i < points.length; i++) {
            points[i] = new Point(i * dataXInterval + left + offset, 0);
        }
    }

    public int getMaxNumOfPoints() {
        return np;
    }

    public boolean isAniming() {
        return animing;
    }

    public int getFlag() {
        return flag;
    }

    public void setFlag(int flag) {
        this.flag = flag;
    }

    public Point getlBot() {
        return lBot;
    }

    public Point getrTop() {
        return rTop;
    }

    public Point getlTop() {
        return lTop;
    }

    public Point getrBot() {
        return rBot;
    }

    public void setDrawCurrentPoint(boolean drawCurrentPoint) {
        this.drawCurrentPoint = drawCurrentPoint;
    }

    public boolean isShowGridding() {
        return showGridding;
    }

    public void setShowGridding(boolean showGridding) {
        this.showGridding = showGridding;
    }

    /**
     * 总共点的个数
     *
     * @return
     */
    public int getNp() {
        return np;
    }

    public void setNp(int np) {
        this.np = np;
    }

    /**
     * 清除数据
     */
    public synchronized void earsePoints() {
        for (int i = 0; i < points.length; ++i) {
            points[i].y = ECGDeviceConstant.ECG_INVALID;
        }
        cur_point = 0;
    }

    public void setFristRound(boolean fristRound) {
        this.fristRound = fristRound;
    }

    /**
     * 更新数据
     *
     * @param v
     * @param point
     */
    public synchronized void updatePoint(float v, int point) {
        if (point < points.length)
            points[point].y = v * svCell * cellNumbPremV;
    }

    long recordTime = -1;

    public synchronized void updatePoint(float v) {
        if (!animing) {
//            points[cur_point] = new Point(cur_point * dataXInterval + left + offset, v * svCell * cellNumbPremV);
            points[cur_point].y = v * svCell * cellNumbPremV;

            if (cur_point + 1 >= np)
                fristRound = false;

            cur_point = (cur_point + 1) % np;
        }
    }

    // 超出部分数据不覆盖之前数据
    public synchronized void updatePointNoCover(float v) {
        if (!animing) {
            points[cur_point].y = v * svCell * cellNumbPremV;

            if (cur_point + 1 > np)
                return;

            cur_point = (cur_point + 1) % np;
        }
    }

    public synchronized void doDraw(Graphics2D g, boolean drawPoint,
                                    Color bgColor, Color gridColor, Color textColor, Color ecgColor) {

        g.setColor(bgColor);
        g.fillRect((int) lTop.x, (int) lTop.y, (int) (rTop.x - lTop.x),
                (int) (lBot.y - lTop.y));

        g.setColor(gridColor);
        drawFrame(g);

        g.setColor(textColor);
        drawText(g);

        drawLegend(g);

        if (!animing) {
            drawLines(points, 0, points.length, g);
            if ((!fristRound || cur_point > 0) && drawPoint) {
                g.setColor(ecgColor);
                drawCurrentPoint(points[cur_point], 3, g);
            }
        }
    }

    private void drawFrame(Graphics2D g) {
        g.setStroke(new BasicStroke(WaveStyle.ECG_FRAME_BOARDER_WIDTH));

        // 最外的框
        g.drawRoundRect((int) rFrame.left, (int) rFrame.top,
                (int) rFrame.width(), (int) rFrame.height(),
                WaveStyle.ECG_FRAME_ARC_SIZE, WaveStyle.ECG_FRAME_ARC_SIZE);

        // 分割线
        if (showGridding) {
            float boarder = WaveStyle.ECG_FRAME_BOARDER_WIDTH;
            g.setColor(WaveStyle.GRID_COLOR);
            g.setStroke(new BasicStroke(WaveStyle.ECG_CELL_BOARDER_WIDTH));

            for (float i = rFrame.top + svCell; i < rFrame.bottom; i += svCell) {
                // if (j % 5 == 0)
                // g.setColor(WaveStyle.W_GRID_COLOR);
                // else
                // g.setColor(WaveStyle.N_GRID_COLOR);
                g.drawLine((int) (left + boarder), (int) i,
                        (int) (right - boarder * 1.5f), (int) i);
            }
            for (float i = rFrame.left + shCell; i < rFrame.right; i += shCell) {
                // if (j % 5 == 0)
                // g.setColor(WaveStyle.W_GRID_COLOR);
                // else
                // g.setColor(WaveStyle.N_GRID_COLOR);
                g.drawLine((int) i, (int) (top + boarder), (int) i,
                        (int) (bottom - boarder * 1.5f));
            }

            float vSpace = svCell / 5;
            float hSpace = shCell / 5;
            for (int i = 1; rFrame.top + i * vSpace < rFrame.bottom; i++) {
                if (i % 5 == 0)
                    continue;
                for (int j = 1; rFrame.left + j * hSpace < rFrame.right; j++) {
                    if (j % 5 == 0)
                        continue;
                    g.setFont((new Font("微软雅黑", Font.PLAIN, 3)));
                    g.drawString("·", (rFrame.left + j * hSpace),
                            (rFrame.top + i * vSpace));
                }
            }

        }
    }

    private void drawText(Graphics2D g2D) {
        g2D.setFont(WaveStyle.TEXT_FONT);
        g2D.drawString(describe, left + WaveStyle.ECG_TEXT_LEFT_MARGIN
                * (rTop.x - lTop.x), top + WaveStyle.ECG_TEXT_TOP_MARGIN
                * (lTop.y - lTop.y) + 20);
    }

    private void drawCurrentPoint(Point p, float radius, Graphics2D g2D) {
        if (!drawCurrentPoint || p == null)
            return;

        float y, maxY;
        maxY = (bottom - top) / 2;
        y = Math.abs(p.y) > maxY ? maxY * Math.signum(p.y) : p.y;
        g2D.fillOval((int) (p.x - radius),
                (int) (-y + (bottom - top) / 2 + top - radius),
                (int) (radius * 2), (int) (radius * 2));
    }

    private void drawLegend(Graphics2D g2D) {
        Point[] points = new Point[6];
        points[0] = new Point(0 * dataXInterval + left, -0.5f * svCell
                * cellNumbPremV);
        points[1] = new Point((0.6f * showFre / cellNumbPreS) * dataXInterval
                + left, -0.5f * svCell * cellNumbPremV);
        points[2] = new Point((0.6f * showFre / cellNumbPreS) * dataXInterval
                + left, 0.5f * svCell * cellNumbPremV);
        points[3] = new Point((1.4f * showFre / cellNumbPreS) * dataXInterval
                + left, 0.5f * svCell * cellNumbPremV);
        points[4] = new Point((1.4f * showFre / cellNumbPreS) * dataXInterval
                + left, -0.5f * svCell * cellNumbPremV);
        points[5] = new Point((2 * showFre / cellNumbPreS) * dataXInterval
                + left, -0.5f * svCell * cellNumbPremV);

        g2D.setColor(WaveStyle.ECG_COLOR);
        g2D.setStroke(new BasicStroke(WaveStyle.ECG_LINE_WIDTH));

        boolean first = true;
        int preX = 0;
        int preY = 0;
        float maxY = (bottom - top) / 2;

        for (int i = 0; i < points.length; i++) {
            Point point = points[i];

            float pointY = Math.abs(point.y) > maxY ? maxY
                    * Math.signum(point.y) : point.y;

            int x = (int) point.x;
            int y = (int) (-pointY + ((bottom - top)) / 2 + top);

            if (first) {
                first = false;
            } else {
                g2D.drawLine(preX, preY, x, y);
            }
            preX = x;
            preY = y;
        }
    }

    protected void drawLines(Point[] points, int start, int end, Graphics2D g2D) {

        g2D.setColor(WaveStyle.ECG_COLOR);
        g2D.setStroke(new BasicStroke(WaveStyle.ECG_LINE_WIDTH));

        boolean first = true;
        int preX = 0;
        int preY = 0;
        float maxY = (bottom - top) / 2;

        for (int i = start; i < cur_point; i++) {
            Point point = points[i];

            if (Math.abs(point.y / svCell / cellNumbPremV) > 30) {
                first = true;
                continue;
            }

            float pointY = Math.abs(point.y) > maxY ? maxY
                    * Math.signum(point.y) : point.y;

            int x = (int) point.x;
            int y = (int) (-pointY + ((bottom - top)) / 2 + top);

            if (first) {
                first = false;
            } else {
                g2D.drawLine(preX, preY, x, y);
            }
            preX = x;
            preY = y;
        }

        if (!fristRound) {
            first = true;
            for (int i = cur_point + 10; i < end; i++) {
                Point point = points[i];
                if (Math.abs(point.y / svCell / cellNumbPremV) > 30) {
                    first = true;
                    continue;
                }

                float pointY = Math.abs(point.y) > maxY ? maxY
                        * Math.signum(point.y) : point.y;

                int x = (int) point.x;
                int y = (int) (-pointY + ((bottom - top)) / 2 + top);

                if (first) {
                    first = false;
                } else
                    g2D.drawLine(preX, preY, x, y);

                preX = x;
                preY = y;
            }
        }
    }

    public boolean isInRect(Integer x, Integer y) {
        return rFrame.isPonitInRect(x, y);
    }
}
